﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Diagnostics.Contracts;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Concurrency;
using System.Threading;

namespace ReactiveCode
{
    using Observables;
    using Statements;
    using Util;

    public class ReactiveMethod
    {
        internal class BodyStatement : ReactiveStatement
        {
            ReactiveStatement _body;

            public BodyStatement(ReactiveStatement body, ReactiveMethod owner)
            {
                Contract.Requires(body != null && owner != null);
                _body = body;
                _body.Parent = this;
                Method = owner;
                BindEnabled(_body);
            }

            public override void Dispose()
            {
                _body.Dispose();
            }
        }

        internal ReactiveStatement Body { get; private set; }
        public bool Enabled
        {
            get { return Body.Enabled; }
        }
        
        public ReactiveHost Host
        {
            get { return Body.Host; }
        }

        internal ReactiveMethod(ReactiveStatement body)
        {
            Contract.Requires(body != null);
            Body = new BodyStatement(body, this);
        }
        public void Dispose()
        {
            Body.Dispose();
        }
    }
    public sealed class ReactiveMethod<T> : ReactiveMethod
    {
        public ReactiveMethod(ReactiveStatement body)
            : base(body)
        {
            CurrentReturnStatementVariable = new Variable<ReturnStatement<T>>();
        }
        public ReactiveMethod(params ReactiveStatement[] body)
            : this(ReactiveStatement.Block(body))
        {

        }

        #region Return value

        internal Variable<ReturnStatement<T>> CurrentReturnStatementVariable { get; private set; }
        internal ReturnStatement<T> CurrentReturnStatement
        {
            get { return CurrentReturnStatementVariable.Value; }
            set { CurrentReturnStatementVariable.Value = value; }
        }
        public IObservable<T> ReturnValue
        {
            get
            {
                return CurrentReturnStatementVariable.SelectMany(r => r != null ? r.Value : Observable.Return(default(T)));
            }
        }

        #endregion
    }
}
